elf loader
今天延續昨天的work,我們要來將elf loader的內容補完,
根據昨天的內容,我們要有能力去判斷我們需要的是elf file內的哪一個content,幸好只要引用"elf.h"就可以幫助我們簡單的得到elf header的格式及內容,昨天我們透過e_entry得到了entrypoint的位置。
在今天,我們要將elf檔案內所要被處理器所執行的內容讀取出來,放到記憶體中。我們會去抓elf檔案內的program header (phdr),phdr內包含了segment的資訊,我們可以透過elf header的e_phum得到對應的section數量,而phdr的格式如下 (參照 linux manual page elf)
typedef struct {
uint32_t p_type;
uint32_t p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} Elf64_Phdr;
其中各欄位的內容大致如下。
属性 | 描述 |
---|---|
p_type | 區段的類型,代表如何解釋該段,有下列數種。 |
PT_NULL | 未使用,該類型允許了有未被使用的區段 |
PT_LOAD | 可加載段,同時也是處理器主要需要的區段 |
PT_DYNAMIC | 動態連結訊息,供連結器使用(Linker) |
PT_INTERP | 紀錄 program interpreter 路徑與檔名。 |
PT_NOTE | 紀錄 Note的位置,不重要。 |
PT_SHLIB | 保留但沒有語意 (reserved but has unspecified semantics)。 |
PT_PHDR | PHDR本身也需要一個位置放 。 |
PT_LOPROC, PT_HIPROC | 範圍內的值用來處理特定的處理器語意,。 |
PT_GNU_STACK | Linux使用的GNU擴充,用來控制stack的狀態。 |
p_offset | 區段的位置的偏移量 (與文件開頭的距離)。 |
p_vaddr | 區段在虛擬記憶體的位置。 |
p_paddr | 區段在實體記憶體的位置的。 |
p_filesz | file所佔的大小。 |
p_memsz | 所需要的memory大小。 |
p_flags | 該區段的權限相關flag: |
PF_X | 可執行。 |
PF_W | 可寫。 |
PF_R | 可讀。 |
p_align | 該區段需要對齊的值。 |
實作
我們只要讀取e_phoffset,知道phdr的位置,再根據每一個section的offset就可以得出我們所需要的區段了,不過在實作我們一樣先加一個測試,測試內容為昨天所讀到的entry point位置(0x10116)對應的內容。
那我們要怎麼知道答案應該是多少呢,我們可以透過objdump得到elf的內容,這裡可以看到0x10116的值是0x00004197。
riscv64-unknown-elf-objdump -d HelloWorld.elf > HelloWorld.objdump
因此我們撰寫的測試內容如下
unsigned get_mem_w(unsigned long long int addr)
{
return *(uint32_t*)(ALISS::memory + addr);
}
TEST(MyTestSuite, ELFLoaderTest_FirstInst) {
const char* test = "test.elf";
ALISS::loadElf(test);
EXPECT_EQ(get_mem_w(0x10016), 0x4197);
}
接下來開始實作啦,實作的程式碼如下
for (int i = 0; i < elfHeader.e_phnum; i++) {
file.seekg(elfHeader.e_phoff + i * sizeof(Elf64_Phdr));
Elf64_Phdr programHeader;
file.read(reinterpret_cast<char*>(&programHeader), sizeof(Elf64_Phdr));
// Check if this is a loadable segment
if (programHeader.p_type == PT_LOAD) {
// Save flags, address, and size
Elf64_Word flags = programHeader.p_flags;
Elf64_Addr addr = programHeader.p_vaddr;
Elf64_Xword size = programHeader.p_memsz;
// Seek to the segment's file offset
file.seekg(programHeader.p_offset);
// Read the segment data to memory
file.read(reinterpret_cast<char*>(ALISS::memory + addr), size);
}
}
如同前面所說的,讀取e_phnum知道區段數量,跳到對應的位置,如果該段的type是PT_LOAD的話則讀到memory內。
新增後測試通過,我們已經實現了一個簡易版的ELF Loader啦~~(灑花)
碎碎念 : 明天終於要開始實作指令了,看能不能趁連假多偷一點進度回來。